/*
 * pcb.c
 *
 *  Created on: May 14, 2013
 *      Author: Edward
 */


#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <pthread.h>
#include "pcb.h"
#include "process.h"
#include "global.h"
#include "queue.h"



pthread_mutex_t lock;

void *runner(void *threadID, PCBPtr this)
{
	PCBPtr p =  (PCBPtr) malloc(sizeof(PCBStr));
	long id = (long) threadID;
	printw("THREAD %ld\n", id);
	int i = 0;
	while( i < 1000){
		pthread_mutex_lock(&lock);
		p = this->dequeueReadyQueue(this);
		p->burst_time--;
		p->toString(p);
		if(p->burst_time == 0){
			printw("P%d has been terminated\n", p->pid);
		}
		if(p->burst_time !=0 ){
			this->setReadyQueue(this, p);
		}
		pthread_mutex_unlock(&lock);
		sleep(3);
		i++;

	}

	pthread_exit(NULL);

	return NULL;
}

/*
 * PCB Constructor.
 */
PCBPtr newPcbConstructor(void) {
	PCBPtr pcb  = (PCBPtr) malloc(sizeof(PCBStr));
	pcb->proc  = newProcessConstructor();
	int i;
	for(i = 0 ; i < PROCESS_SIZE; i++){
		pcb->ready_queue[i] = (PCBPtr) malloc(sizeof(PCBStr));
		pcb->waiting_queue[i] = (PCBPtr) malloc(sizeof(PCBStr));
	}
	pcb->size_r = 0;
	pcb->size_w = 0;
	pcb->back = -1;
	pcb->front = 0;
	pcb->next_step = 0;
	//pcb->owns = 0;
	pcb->pid = 1;
	pcb->state = READY;
	pcb->waiting_on = 0;
	pcb->burst_time = 5;
	pcb->priority = 0;
	pcb->time_slice = 0;
	pcb->my_counter = 0;

	//Pointers to functions.
	pcb->setPID = setPID;
	pcb->getPID = getPID;
	pcb->setState = setState;
	pcb->getState = getState;
	pcb->setTimeSlice = setTimeSlice;
	pcb->getTimeSlice = getTimeSlice;
	pcb->setNextStep = setNextStep;
	pcb->getNextStep = getNextStep;
	pcb->setBurstTime = setBurstTime;
	pcb->getBurstTime = getBurstTime;
	pcb->createProcess = createProcess;
	pcb->setPriority = setPriority;
	pcb->getPriority  = getPriority;

	//	pcb->ownsMutex = ownsMutex;
	pcb->setReadyQueue = setReadyQueue;
	pcb->getReadyQueue = getReadyQueue;
	pcb->dequeueReadyQueue = dequeueReadyQueue;
	pcb->dequeueWaitingQueue = dequeueWaitingQueue;
	pcb->setIOQueue = setIOQueue;
	pcb->getIOQueue = getIOQueue;

	pcb->toString = toString;
	pcb->stepProcess = stepProcess;
	
	return pcb;
}

/*
 * Set process Time slice
 * this - the PCBPtr
 * time_slice - the process time_slice
 */
int setTimeSlice(PCBPtr this, int time_slice) {
	this->time_slice = time_slice;
	return NO_ERROR;
}
/*
 * Return process Time slice
 */
int getTimeSlice(PCBPtr this) {
	return this->time_slice;
}

/*
 * Set process Priority
 * this - PCBPtr
 * priority - the process priority
 */
int setPriority(PCBPtr this, int priority) {
	this->priority = priority;
	return NO_ERROR;
}
/*
 * Return process Priority
 * this - the PCBPtr
 */
int getPriority(PCBPtr this) {
	return this->priority;
}

int setBurstTime(PCBPtr this, int burst_time) {
	this->burst_time = burst_time;
	return NO_ERROR;
}
/*
 * Return process Burst time
 * this - the PCBPtr
 */
int getBurstTime(PCBPtr this) {
	return this->burst_time;
}
/*
 * Set process ID
 * this - the PCBPtr
 * id - the process ID
 */
int setPID(PCBPtr this, int id) {
	this->pid = id;
	return NO_ERROR;
}
/*
 * Return process ID
 * this - the PCBPtr
 */
int getPID(PCBPtr this) {
	return this->pid;
}

/*
 * Set sets of a process
 * this - the PCBPtr
 * state - the process state
 */
int setState(PCBPtr this, int state) {
	this->state = state;
	return NO_ERROR;
}

/*
 * Return state  of a process
 * this - the PCBPtr
 */
int getState(PCBPtr this) {
	return this->state;
}

/** this is the step count that the CPU had gotten to when this process was
  prempted (like a PC register value)
  this - the PCPtr
  nextStep - the step of the process
 */
int setNextStep(PCBPtr this, int nextStep) {
	this->next_step = nextStep;
	return NO_ERROR;

}
/*
 * Return next step  of a process
 * this - the PCBPtr
 */
int getNextStep(PCBPtr this) {
	return this->next_step;

}
/*
 *Add PCB to ready queue, return null if the PCBPtr is empty or overflow
 *if the ready queue is empty
 *this - the PCBPtr
 *process - the process
 */
int setReadyQueue(PCBPtr this, PCBPtr process) {
	if(this == NULL || process == NULL){
		return NULL_POINTER_ERROR;
	}
	else if(this->back == PROCESS_SIZE){
		printw("\nOVERFLOW!!!");
	}
	else{
	//	pthread_mutex_lock(&lock);
		//if(this->state == READY){
			this->back = (this->back + 1) % PROCESS_SIZE;
			this->ready_queue[this->back] =   process;
			this->size_r++;
		//	pthread_mutex_unlock(&lock);
	//	}
	}

	return NO_ERROR;
}
/*
 *Dequeue PCB from ready queue and checks for underflow of the ready queue
 *this - the PCBptr
 */
PCBPtr dequeueReadyQueue(PCBPtr this) {
	//pthread_mutex_lock(&lock);
	PCBPtr temp  = this->ready_queue[this->front];
	if(this->back == -1){
		printw("");
	}
	else{
		this->front=(this->front + 1) % MAX_QUEUE_SIZE;
		this->size_r--;
	}
	//pthread_mutex_unlock(&lock);
	return temp;
}
/*
 *Return PCB ready queue, return null when the PCBPtr is null and checks if the ready
 *queue is empty
 *this - the PCBptr
 */
PCBPtr getReadyQueue(PCBPtr this) {
	int i;
	PCBPtr temp[PROCESS_SIZE];
	//pthread_mutex_lock(&lock);
	if(this->front == this->back){
		printw("\nQueue is Empty!!!");
	}
	else
	{
		for(i = this->front;  i <= this->back; i++){
			temp[i] = (PCBPtr) this->ready_queue[i];
		}
	}
	//pthread_mutex_unlock(&lock);
	return temp;
}
/*
 *Dequeue PCB from waiting queue and checks for underflow
 *this - the PCBPtr
 */
PCBPtr dequeueWaitingQueue(PCBPtr this) {
	//pthread_mutex_lock(&lock);
	PCBPtr temp  = this->waiting_queue[this->front];
	if(this->back == -1){
		printw("");
	}
	else{
		this->front=(this->front + 1) % MAX_QUEUE_SIZE;
		this->size_w--;
	}
	//pthread_mutex_unlock(&lock);
	return temp;
}
/*
 *Add PCB IO queue and checks for overflow
 *this - the PCBPtr
 *process - the process
 */
int setIOQueue(PCBPtr this, PCBPtr process) {
	if(this == NULL || process == NULL){
		return NULL_POINTER_ERROR;
	}
	else if(this->back == PROCESS_SIZE){
		printw("\nOVERFLOW!!!");
	}
	else{
	//	pthread_mutex_lock(&lock);
		if(process->state == BLOCKED){
			this->back = (this->back + 1) % PROCESS_SIZE;
			this->waiting_queue[this->back] =   process;
			this->size_w++;
		}
	//	pthread_mutex_unlock(&lock);
	}
	return NO_ERROR;
}
/*
 *Return PCB IO queue
 *this - the PCBPtr
 */
PCBPtr getIOQueue(PCBPtr this) {
	int i;
	PCBPtr temp[PROCESS_SIZE];
	if(this->front == this->back){
		printw("\nQueue is Empty!!!");
	}
	else{
	//	pthread_mutex_lock(&lock);
		for(i = this->front;  i <= this->back; i++){
			temp[i] = (PCBPtr) this->waiting_queue[i];
		}
	//	pthread_mutex_unlock(&lock);
	}
	return temp;
}

/**
 * Creates a process
 * this - the PCBPtr
 * processID - the process ID
 * burst_time - the burst time of the process
 * state - the state of the process
 */
int createProcess(PCBPtr this, int processID, int burst_time, int state){
	this->pid = processID;
	this->burst_time = burst_time;
	this->state = state;
	return NO_ERROR;
}
/**
 * Object representation of the PCB
 */
int toString(PCBPtr this){
	char * state;
	switch(this->state){
	case READY: state = "ready";
	break;
	case RUNNING: state = "running";
	break;
	case INTERRUPTED: state = "interrupted";
	break;
	case BLOCKED:  state = "blocked";
	break;
	case TERMINATED: state = "terminated";
	break;
	default: state = "unknown";
	break;
	}
	char * s;
	switch(this->proc->proc_type){
	case COMPUTE: s = "compute";
	break;
	case IO: s = "io";
	break;
	case KEYBOARD: state = "keyboard";
	break;
	default: s = "xxxx";
	break;
	}
	char * r;
	switch(this->proc->no_requests){
	case IO_SERVICE: r = "io service";
	break;
	case KEYBOARD_SERVICE: r = "keyboard service";
	break;
	case NETWORK_SERVICE: state = "network service";
	break;
	default: r = "xxxx";
	break;
	}
	printw("P%d - %s Burst Time:%d Type:%s Request:%s Steps:%d\n", this->pid, state, this->burst_time,
			s, r, this->proc->no_steps);
	return NO_ERROR;
}
/*
 * Excute process
 * this - the PCBPtr
 * timeSlot - the cpu time slot
 */
void executeProcess(PCBPtr this, int timeSlot)
{
	if((this->burst_time-timeSlot)>0)
	{
		printw("P%d Burst time remain is %d to finish\n",this->pid, this->burst_time-timeSlot);
	}else
	{
		this->state = TERMINATED;
		printw("P%d is DONE\n",this->pid);
	}
}

int stepProcess(PCBPtr this) {
	int event = -1;  //NO_EVENT
//printw("[err]...watch this... ");
	this->my_counter++;
	if((this->my_counter % this->burst_time) == 0) {
		event = this->proc->getProcType(this->proc);
	}
	return event;
}

//int main(void)
//{
//	void * exit;
//	PCBPtr pcb = newPcbConstructor();
////	PCBPtr p = newPcbConstructor();
////
////	PCBPtr p1 = newPcbConstructor();
////	PCBPtr p2 = newPcbConstructor();
////	PCBPtr p3 = newPcbConstructor();
////	PCBPtr p4 = newPcbConstructor();
//	PCBPtr p5= newPcbConstructor();
//	PCBPtr p6 = newPcbConstructor();
//	//			PCBPtr p7 = newPcbConstructor();
//	//				PCBPtr p8 = newPcbConstructor();
//	//				PCBPtr p9 = newPcbConstructor();
//	//					PCBPtr p10 = newPcbConstructor();
//	//					PCBPtr p11 = newPcbConstructor();
//	//						PCBPtr p12 = newPcbConstructor();
//
////	p1->setPID(p1, 1);
////	p1->setBurstTime(p1, 1);
////	p1->state = READY;
////	p1->proc->no_requests = 1;
////	p1->proc->no_steps = 2;
////	p1->proc->proc_type = 1;
////	pcb->setReadyQueue(pcb, p1);
////
////
////	p2->setPID(p2, 2);
////	p2->setBurstTime(p2, 2);
////	p2->state = READY;
////	p2->proc->no_requests = 0;
////	p2->proc->no_steps = 1;
////	p2->proc->proc_type = 0;
////	pcb->setReadyQueue(pcb, p2);
////
////	p3->setPID(p3, 3);
////	p3->setBurstTime(p3, 3);
////	p3->state = READY;
////	p3->proc->no_requests = 1;
////	p3->proc->no_steps = 2;
////	p3->proc->proc_type = 1;
////	pcb->setReadyQueue(pcb, p3);
////
////
////	p4->setPID(p4, 4);
////	p4->setBurstTime(p4, 4);
////	p4->state = READY;
////	p4->proc->no_requests = 0;
////	p4->proc->no_steps = 1;
////	p4->proc->proc_type = 0;
////	pcb->setReadyQueue(pcb, p4);
////
//	p5->setPID(p5, 5);
//	p5->setBurstTime(p5, 5);
//	p5->state = READY;
//	p5->proc->no_requests = 1;
//	p5->proc->no_steps = 2;
//	p5->proc->proc_type = 1;
//	//pcb->setReadyQueue(pcb, p5);
//	//p5->toString(p5);
//
//
//	p6->setPID(p6,6);
//	p6->setBurstTime(p6, 6);
//	p6->state = BLOCKED;
//	p6->proc->no_requests = 0;
//	p6->proc->no_steps = 1;
//	p6->proc->proc_type = 0;
//	pcb->setIOQueue(pcb, p6);
//   // p6->toString(p6);
//    PCBPtr x = pcb->waiting_queue[0];
//    x->toString(x);
//
//	pthread_t thread_ID[5];
//	int i;
////	pthread_mutex_init(&lock, NULL);
////	for(i = 0;i < 1000; i++)
////	{
////		pthread_create(&thread_ID[i], NULL, runner(NULL, pcb), NULL);
////	}
////	for(i = 0;i < 1000; i++)
////	{
////		pthread_join(thread_ID[i], &exit);
////	}
//
//
//	return 0;
//}

/*
 //	printw("%d\n ", pcb->ready_queue->Queue[0]);

//	pcb->setPID(pcb, 56);
//	pcb->setReadyQueue(pcb);
//	pcb->state = BLOCKED;
//	printw("%d\n ", pcb->ready_queue->Queue[1]);
//
//	pcb->ready_queue->display(pcb->ready_queue);


	//	printw("f%d\n", pcb->ready_queue->Queue[pcb->ready_queue->front]);
	//	printw("b%d\n", pcb->ready_queue->Queue[pcb->ready_queue->back]);
//	       int i;
//		      int* x;
//		     x = pcb->getReadyQueue(pcb);
//	          for(i = 0; i < 2; i++){
//	        	   printw("M%d\n", x[i]);
//
//	           }

	//executeProcess(pcb, 1);



 */
